/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* $Id: BalancingColumnBreakingAlgorithm.java,v 1.1 2007/04/12 06:41:19 cvsuser Exp $ */

package com.wisii.wisedoc.layoutmanager;

import com.wisii.wisedoc.log.LogUtil;
import com.wisii.wisedoc.traits.MinOptMax;

/**
 * This is a the breaking algorithm that is responsible for balancing columns in
 * multi-column layout.
 */
public class BalancingColumnBreakingAlgorithm extends PageBreakingAlgorithm {

	private int columnCount;
	private int fullLen;
	private int idealPartLen;

	public BalancingColumnBreakingAlgorithm(LayoutManager topLevelLM,
			PageSequenceLayoutManager.PageProvider pageProvider,
			PageBreakingLayoutListener layoutListener, int alignment,
			int alignmentLast, MinOptMax footnoteSeparatorLength,
			boolean partOverflowRecovery, int columnCount) {
		super(topLevelLM, pageProvider, layoutListener, alignment,
				alignmentLast, footnoteSeparatorLength, partOverflowRecovery,
				false, false);
		this.columnCount = columnCount;
		this.considerTooShort = true; // This is important!
	}

	/** @see com.wisii.wisedoc.layoutmanager.BreakingAlgorithm */
	protected double computeDemerits(KnuthNode activeNode,
			KnuthElement element, int fitnessClass, double r) {
		double dem = super
				.computeDemerits(activeNode, element, fitnessClass, r);
		LogUtil.debug("original demerit=" + dem + " " + totalWidth + " line="
				+ activeNode.line + "/" + columnCount + " pos="
				+ activeNode.position + "/" + (par.size() - 1));
		int remParts = columnCount - activeNode.line;
		int curPos = par.indexOf(element);
		if (fullLen == 0) {
			fullLen = ElementListUtils.calcContentLength(par,
					activeNode.position, par.size() - 1);
			this.idealPartLen = (fullLen / columnCount);
		}
		int partLen = ElementListUtils.calcContentLength(par,
				activeNode.position, curPos - 1);
		int restLen = ElementListUtils.calcContentLength(par, curPos - 1, par
				.size() - 1);
		int avgRestLen = 0;
		if (remParts > 0) {
			avgRestLen = restLen / remParts;
		}
		LogUtil.debug("remaining parts: " + remParts + " rest len: " + restLen
				+ " avg=" + avgRestLen);
		double balance = (idealPartLen - partLen) / 1000f;
		LogUtil.debug("balance=" + balance);
		double absBalance = Math.abs(balance);
		dem = absBalance;
		// Step 1: This does the rough balancing
		if (columnCount > 2) {
			if (balance > 0) {
				// shorter parts are less desired than longer ones
				dem = dem * 1.2f;
			}
		} else {
			if (balance < 0) {
				// shorter parts are less desired than longer ones
				dem = dem * 1.2f;
			}
		}
		// Step 2: This helps keep the trailing parts shorter than the previous
		// ones
		dem += (avgRestLen) / 1000f;

		if (activeNode.line >= columnCount) {
			// We don't want more columns than available
			dem = Double.MAX_VALUE;
		}
		LogUtil.debug("effective dem=" + dem + " " + totalWidth);
		return dem;
	}
}
